<template>
  <teleport to="body">
    <!-- 遮罩层 -->
    <transition name="fade">
      <div
        v-if="modelValue"
        class="fixed inset-0 z-50 bg-black opacity-50 transition-opacity"
        @click.self="close"
      />
    </transition>

    <!-- 弹窗内容 -->
    <transition name="slide">
      <div
        v-if="modelValue"
        ref="contentRef"
        class="fixed bottom-0 left-0 right-0 z-50 bg-bg-black-7 rounded-t-xl shadow-xl max-h-[80vh] overflow-hidden flex flex-col"
      >
        <!-- 头部 -->
        <div class="sticky top-0 px-4 bg-bg-black-7 z-10 my-[0.875rem]">
          <div class="flex justify-center items-center relative">
            <h3 class="text-text-white-1 font-[700] text-[.875rem]">
              {{ title }}
            </h3>
            <button
              @click="close"
              type="button"
              class="absolute right-0 bg-bg-black-5 size-[1.5rem] flex justify-center items-center rounded-[.375rem] top-0"
              aria-label="关闭"
            >
                <BaseIcon name="uni-close"/>
            </button>
          </div>

          <!-- 搜索框 -->
          <div class="relative pt-[.75rem]" v-if="showSearch">
            <BaseInput name="search" search v-model="searchQuery" :placeholder="searchPlaceholder"></BaseInput>
          </div>
        </div>
        <slot name="sheet-header"></slot>

        <!-- 内容区域 -->
        <div class="flex-1 overflow-y-auto mt-[1rem]">
          <template v-if="Array.isArray(items)">
            <ul class="pb-[3.125rem]" v-if="filteredItems?.length">
              <!-- 每个 li 都通过 slot 传入 -->
              <slot
                v-for="(item, index) in filteredItems"
                :item="item"
                :index="index"
                :close="close"
              />
            </ul>
            <BaseEmpty v-else/>
          </template>
          <slot v-else/>
        </div>
      </div>
    </transition>
  </teleport>
</template>

<script setup lang="ts">

defineOptions({
  name: 'BaseSheet',
})

interface Props {
  modelValue: boolean
  title?: string
  items?: any
  showSearch?: boolean
  searchPlaceholder?: string
  emptyText?: string
  filterKey?: string,
  forceOpen?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  title: '请选择',
  items: '',
  showSearch: false,
  searchPlaceholder: '搜索',
  emptyText: '没有找到匹配项',
  filterKey: '',
  forceOpen: false
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: boolean): void
  (e: 'select', item: any): void
}>()

const searchQuery = ref('')
const contentRef = ref<HTMLElement | null>(null)

// 过滤后的列表项
const filteredItems = computed(() => {
  if (!props.showSearch || !searchQuery.value) return props.items
  return props.items.filter(item => (item[props.filterKey])?.toLowerCase()?.includes(searchQuery.value))
})

// 关闭弹窗
const close = () => {
  if(props.forceOpen) return
  searchQuery.value = ''
  emit('update:modelValue', false)
}

// 键盘事件监听
const handleKeydown = (event: KeyboardEvent) => {
  if (event.key === 'Escape') {
    close()
  }
}

// 监听打开/关闭状态变化
watch(() => props.modelValue, (newVal) => {
  if (newVal) {
    document.addEventListener('keydown', handleKeydown)
  } else {
    document.removeEventListener('keydown', handleKeydown)
  }
})

// 组件卸载时移除事件监听
onUnmounted(() => {
  document.removeEventListener('keydown', handleKeydown)
})
</script>

<style scoped>
/* 遮罩层淡入淡出动画 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/* 内容滑动动画 */
.slide-enter-active {
  animation: slide-up 0.3s ease-out;
}

.slide-leave-active {
  animation: slide-down 0.3s ease-in;
}

@keyframes slide-up {
  from {
    transform: translateY(100%);
  }
  to {
    transform: translateY(0);
  }
}

@keyframes slide-down {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(100%);
  }
}
</style>